/********************************************************************
* V0Filter.cpp: Impletement class V0Filter.
*
* Description:
*   Optimize V0 const (value equals 0) in IL.
*
* History:
*   Create: gxf. 2008-11-15
*   
*   Last Modified:
*           gxf. 2008-12-10
*
*******************************************************************/

#include "crossbit/VFilter.h"
#include "crossbit/VInst.h"
#include "crossbit/VBlock.h"
#include "crossbit/GLOBAL.h"
#include "crossbit/UTIL.h"

using namespace crossbit;

static inline void DeleteInst(VBlock::vb_iterator& Itr, VBlock* pBlock)
{
    VInst * inst = *Itr;
    Itr = pBlock -> erase(Itr);
    delete inst;
}


XTUint32 V0Filter::run(VBlock *vb)
{
    VBlock::vb_iterator Itr = vb->begin(); 
    VBlock::vb_iterator End = vb->end(); 

    XTRegNum reNameTable[GLOBAL::MAX_VREG];
    UTIL::XTMemset(reNameTable, GLOBAL::MAX_VREG + 1, sizeof(reNameTable));

    bool unfinished = true;;
    while (unfinished)
    {
        unfinished = false;

        while(Itr != End){
            XTRegNum sreg;
            XTRegNum vreg;
            XTRegNum vreg1, vreg2, vreg3, vreg4;

            VInst *inst = *Itr;

            switch(inst -> getOpcode()){
                case OP_LI:
                    break;
                case OP_GET: 
                    sreg = inst->getOperand(1).reg;  //sreg for source reg 
                    vreg = inst->getOperand(2).reg;  //vreg for v reg 

                    // Delete any v0 reg mapping in
                    if (0 == vreg){
                        DeleteInst(Itr, vb);
                        Itr--;
                    }
                    else if (0 == sreg){
                        assert(vreg != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg));
                        unfinished = true;
                    }
                    break;
                case OP_PUT:
                case OP_MOV:
                case OP_LD:
                case OP_ST:
                case OP_SEXT:
                case OP_ZEXT:
                case OP_NOT:
                    break;
                case OP_ADD:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;

                    if (vreg1 == 0 && vreg2 != 0){ 
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg2, vreg3));
                    }
                    else if (vreg1 != 0 && vreg2 == 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg1, vreg3));
                    }
                    else if (vreg1 == 0 && vreg2 == 0){
                        assert(vreg3 != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        unfinished = true;
                    }
                    break;

                case OP_SUB:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;

                    if (vreg1 == 0 && vreg2 != 0){
                    // TODO: bug fix - locate why this replacement cannot happen
/*                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, NOT(vreg2, vreg3));
*/                    }
                    else if (vreg1 != 0 && vreg2 == 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg1, vreg3));
                    }
                    else if (vreg1 == 0 && vreg2 == 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        unfinished = true;
                    }
                    break;

                case OP_AND:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;

                    if (vreg1 == 0 || vreg2 == 0){
                        assert(vreg3 != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        unfinished = true;
                    }
                    break;

                case OP_XOR:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;

                    if ((vreg1 == 0 && vreg2 != 0)){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg2, vreg3));
                    }
                    else if (vreg1 != 0 && vreg2 == 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg1, vreg3));
                    }
                    break;
                case OP_OR:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;

                    if (vreg1 == 0 && vreg2 != 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg2, vreg3));
                    }
                    else if (vreg1 != 0 && vreg2 == 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg1, vreg3));
                    }
                    else if (vreg1 == 0 && vreg2 == 0){
                        assert(vreg3 != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        unfinished = true;
                    }
                    break;
                case OP_SLL:
                case OP_SRL:
                case OP_SRA:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;

                    if (vreg1 == 0 && vreg2 != 0){
                        assert(vreg3 != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        unfinished = true;
                    }
                    else if (vreg1 != 0 && vreg2 == 0){
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, MOV(vreg1, vreg3));
                    }
                    break;
                    /* TODO: Seems not supported by translator yet
                    case OP_SLLV:
                        break;
                    case OP_SRLV:
                        break;
                    case OP_SRAV:
                        break; 
                    */
                case OP_MUL:
                case OP_MULU:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;
                    vreg4 = inst->getOperand(4).reg;

                    if (vreg1 == 0 || vreg2 == 0){
                        assert(vreg3 != 0);
                        assert(vreg4 != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        Itr = vb -> insert(Itr, LI(0, vreg4));
                        unfinished = true;
                    }
                    break;
                case OP_DIV:
                case OP_DIVU:
                    vreg1 = inst->getOperand(1).reg;  
                    vreg2 = inst->getOperand(2).reg; 
                    vreg3 = inst->getOperand(3).reg;
                    vreg4 = inst->getOperand(4).reg;

                    if (vreg1 == 0){
                        assert(vreg3 != 0);
                        assert(vreg4 != 0);
                        DeleteInst(Itr, vb);
                        Itr = vb -> insert(Itr, LI(0, vreg3));
                        Itr = vb -> insert(Itr, LI(0, vreg4));
                    }
                    break;
                case OP_CMP:
                case OP_JMP:
                case OP_BRANCH:
                case OP_SYSCALL:
                case OP_CALL:
                case OP_HALT:
                    break;
                default: 
                    // Unsupported inst in
//                    assert(0);
                    break;
            }
            Itr++;
        }// while
    } // while unfinished
    
	return (XTUint32)1;
}

